#include <stdio.h>
#include <EEPROM.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include "structs.h"
#include "mpr121.h"
#include "i2c.h"

// AE7JO-TK1 Arduino Based Capacitive Touch Keyer
//
// Copyright 2011 Ira Burton, AE7JO [ http://www.ae7jo.net ]
// 
// All trademarks referred to in source code and documentation are copyright their respective owners.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//----[ Version Defines ]--------------------------------------------------------------------------------------------------------------------------
#define FIRMWARE_VERSION "000.01"
#define HARDWARE_VERSION    "AAA"

//----[ Hardware Defines ]-------------------------------------------------------------------------------------------------------------------------
#define PIN_SIDETONE               11              // Speaker line to transmit sidetone
#define PIN_MPR121_IRQ              2              // Incoming interrupt pin from the MPR121 Module this should not change due to the way attachInterrupt works
#define PIN_DIT_PADDLE              3
#define PIN_DAH_PADDLE              4
#define MPR121_R                 0xB5	           // ADD pin is grounded
#define MPR121_W                 0xB4	           // So address is 0x5A

//----[ Feature Defines ]--------------------------------------------------------------------------------------------------------------------------
#define FEATURE_PADDLE_MPR121                      // If this is defined, use capacitive touch, otherwise it will be standard hardware based

//----[ Defaults Defines ]-------------------------------------------------------------------------------------------------------------------------
#define DEFAULT_BAUD_RATE        9600
#define DEFAULT_SIDETONE_PLAY    true
#define DEFAULT_SIDETONE_HZ       600              // Frequency (in Hz.) of the audible sidetone
#define DEFAULT_WPM                20              // The default sending words per minute based on PARIS timing (Must be at least 5 WPM)
#define EEPROM_START             0x00

//----[ Misc. Defines ]----------------------------------------------------------------------------------------------------------------------------
#define ALERT_HIGH_HZ             900              // Frequency (in Hz.) of the alert high signal
#define ALERT_LOW_HZ              400              // Frequency (in Hz.) of the alert low signal

#define MORSE_DAH                 300              // Timing of a dah compared to a dit (which is always one unit) 300 = 3 to 1
#define MORSE_WORD_GAP            700              // Timing of the space between words relative to dit, 700 = 7 to 1
#define BUFFER_LENGTH             256              // The number of bytes in the send buffer, this should always be 256 as the code makes heavy
                                                   //   assumptions and uses automatic overflow to handle some math to optimize the code.
                                                   //   If you want to change this, you will have to modify the buffer_push and buffer_pop functions.
//----[ Conditional Defines ]----------------------------------------------------------------------------------------------------------------------                                                  
#ifndef FEATURE_PADDLE_MPR121
  #define FEATURE_PADDLE_STD
#endif

//----[ State Variables ]--------------------------------------------------------------------------------------------------------------------------
boolean settings_changed = false;                  // Determines if the settings structure has changed and need to be resaved
SettingStruct settings;                            // Holds the persistant variables that are stored to eeprom
ButtonStates  buttons;                             // Holds the value of the mpr121 buttons last read in;

//----[ Operating Variables ]----------------------------------------------------------------------------------------------------------------------
byte morse_dit_len;                                // The number of MS for a morse dit
int  morse_dah_len;                                // The number of MS for a dah
int  morse_space_len;                              // The number of MS for a word space
byte bufferData[BUFFER_LENGTH];                    // The buffer that stores the data that is being sent
byte bufferStart;                                  // A pointer to the start of the buffer data
byte bufferEnd;                                    // A pointer to the end of the bufer data   


void setup() {
  setup_pin(PIN_MPR121_IRQ, INPUT, HIGH);
  setup_buttons();
  check_buttons();
  
  setup_pin(PIN_SIDETONE, OUTPUT, LOW);
  
  Serial.begin(DEFAULT_BAUD_RATE);
  Serial.print("AE7JO-TK1 Keyer -- Firmware: ");
  Serial.print(FIRMWARE_VERSION);
  Serial.print(" Hardware: ");
  Serial.print(HARDWARE_VERSION);
  Serial.println("");

  eeprom_read_settings();
  buffer_clear();
}

void loop() {
  //buffer_process();
  check_buttons();
  button_process();
}

// ---[ alert1 ]-------------------------------------------------------------------------------------------------------------------------------
//     Purpose: Play an error tone on the speaker
//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void alert1() {
  tone(PIN_SIDETONE, ALERT_LOW_HZ, 300);        // Reminder: Tone is non-blocking
}

// ---[ alert2 ]-------------------------------------------------------------------------------------------------------------------------------
//     Purpose: Play an error tone on the speaker//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void alert2() {
  tone(PIN_SIDETONE, ALERT_HIGH_HZ, 300);      // Reminder: Tone is non-blocking
}

// ---[ alert3 ]-------------------------------------------------------------------------------------------------------------------------------
//     Purpose: Play an error tone on the speaker
//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void alert3() {
  tone(PIN_SIDETONE, ALERT_LOW_HZ, 100);      // Reminder: Tone is non-blocking
  delay(120);                             
  tone(PIN_SIDETONE, ALERT_HIGH_HZ, 100);
  delay(120);
  tone(PIN_SIDETONE, ALERT_LOW_HZ, 100);
}

// ---[ buffer_clear ]-------------------------------------------------------------------------------------------------------------------------
//     Purpose: Set the buffer variables to a state that would indicate an empty buffer
//   Changelog: 19-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void buffer_clear() {
  bufferStart = 0;
  bufferEnd = 0;
}

// ---[ buffer_pop_bit ]-----------------------------------------------------------------------------------------------------------------------
//     Purpose: Pull the next available bit from the front of the buffer and remove it.  (Buffer is a FIFO)
//     Returns: 0 - No data available
//              1 - Dit
//              2 - Dah
//   Changelog: 19-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
byte buffer_pop_bit() {
  if (bufferStart == bufferEnd) {                          // Step 1 - See if there is any data to process
    return 0;                                              //           if not, return 0 and exit
  } else {
    byte value = bufferData[bufferStart];                  // Look at the first byte in the buffer
    byte returnVal;
    
    if (value & 1) {                                       // If the next bit is 1 we will return a 2 (after processing completes)
      returnVal = 2;
    } else {                                               // Otherwise we will return a 1 (after processing completes)
      returnVal = 1;
    }
    
    value = value / 2;                                     // Shift one bit to the right
    
    if(value > 1) {                                        // If the remaining value is greater than 1, then there is more data to process
      bufferData[bufferStart] = value;                     //   so store it for later use
    } else {                                               // Otherwise, increment the start pointer
      bufferStart += 1;
    }
    
    return returnVal;                                      // And finally return the value calculated above
  }
}

// ---[ buffer_process ]-----------------------------------------------------------------------------------------------------------------------
//     Purpose: Handle sending any data in the send buffer
//   Changelog: 19-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void buffer_process() {
  byte value = buffer_pop_bit();
  if (value > 0) {
    if (value == 1) {
      Serial.println("DIT");
    } else {
      Serial.println("DAH");
    }
  }
}

// ---[ buffer_push ]--------------------------------------------------------------------------------------------------------------------------
//     Purpose: Place a value at the end of the send buffer (Buffer is a FIFO)
//   Changelog: 19-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void buffer_push(byte value) {
  bufferData[bufferEnd] = value;
  bufferEnd += 1;
}

// ---[ button_process ]-----------------------------------------------------------------------------------------------------------------------
//     Purpose: Handle the button press information
//   Changelog: 19-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void button_process() {
  if (buttons.paddleLeft) {
    Serial.println("LEFT");
    tone(PIN_SIDETONE, settings.sidetoneHZ);      // Reminder: Tone is non-blocking
    delay(morse_dit_len);
    noTone(PIN_SIDETONE);
    delay(morse_dit_len);
  }
  if (buttons.paddleRight) {
    Serial.println("RIGHT");
    tone(PIN_SIDETONE, settings.sidetoneHZ);      // Reminder: Tone is non-blocking
    delay(morse_dah_len);
    noTone(PIN_SIDETONE);
    delay(morse_dit_len);
  }
}

// ---[ calculate_operating_variables ]--------------------------------------------------------------------------------------------------------
//     Purpose: Calculate the various operating variables that need to be set after a factory reset or change of parameter
//   Changelog: 19-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void calculate_operating_variables() {
  morse_dit_len   = 1200 / settings.WPM;                           // 1200 is used because of the amateur preferece for PARIS timing (wikipedia)
  morse_dah_len   = long(morse_dit_len * MORSE_DAH) / 100 ;  
  morse_space_len = long(morse_dit_len * MORSE_WORD_GAP) / 100;
}

// ---[ eeprom_factory_settings ]--------------------------------------------------------------------------------------------------------------
//     Purpose: Set the eeprom settings to factory default
//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void eeprom_factory_settings() {
  settings.EEPROMVersion = EEPROM_VERSION;
  settings.sidetonePlay  = DEFAULT_SIDETONE_PLAY;
  settings.sidetoneHZ    = DEFAULT_SIDETONE_HZ;
  settings.WPM           = DEFAULT_WPM;
  eeprom_write_settings();
  calculate_operating_variables();
  alert3();
  alert3();
}

// ---[ eeprom_read_settings ]-----------------------------------------------------------------------------------------------------------------
//     Purpose: Load the saved settings for the keyer from EEPROM
//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void eeprom_read_settings() {
  for(unsigned int i=0; i<sizeof(settings); i++)
    *((char*)&settings + i) = EEPROM.read(EEPROM_START + i);   
  if(settings.EEPROMVersion != EEPROM_VERSION)
    eeprom_factory_settings();
  settings_changed = false;
  calculate_operating_variables();
}

// ---[ eeprom_write_settings ]----------------------------------------------------------------------------------------------------------------
//     Purpose: Write the settings for the keyer to EEPROM
//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void eeprom_write_settings() {  
  for(unsigned int i=0; i<sizeof(settings); i++)
    EEPROM.write(EEPROM_START + i, *((char*)&settings + i));
  
  settings_changed = false;
}

// ---[ check_buttons ]---------------------------------------------------------------------------------------------------------------------
//     Purpose: Checks to see if there have been changes to the MPR121 inputs.  It reads the capacitive values and stores them in the
//              buttons variable.  It should be efficient, as it is called whenever a button (or paddle) changes state.
//   Changelog: 20-NOV-2011 - IB - Function in now pulled instead of interrupted as delay and Tone do not work in interrupts.
//              18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
#ifdef FEATURE_PADDLE_MPR121
void check_buttons() {
  if (digitalRead(PIN_MPR121_IRQ) == LOW) {
    unsigned int state_buttons = mpr121_read(0x01) << 8;
    unsigned long callTime = millis();
    state_buttons |= mpr121_read(0x00);
    
    if((state_buttons & 0x01) == 0x01) {
      buttons.paddleLeft = true;
      buttons.leftPressTime = callTime;
    } else {
      buttons.paddleLeft = false;
    }
    
    if((state_buttons & 0x02) == 0x02) {
      buttons.paddleRight = true;
      buttons.rightPressTime = callTime;
    } else {
      buttons.paddleRight = false;
    }
  
    buttons.buttonValue = state_buttons >> 2;
    if (buttons.buttonValue > 0) {
      buttons.buttonPressTime = callTime;
    }
  }
}
#else
// ---[ check_buttons ]---------------------------------------------------------------------------------------------------------------------
//     Purpose: Read the paddles
//   Changelog: 23-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void check_buttons() {
  unsigned long callTime = millis();
  if(digitalRead(PIN_DIT_PADDLE) == 0x00) {
    buttons.paddleLeft = true;
    buttons.leftPressTime = callTime;
  } else {
    buttons.paddleLeft = false;
  }
  
  if(digitalRead(PIN_DAH_PADDLE) == 0x00) {
    buttons.paddleRight = true;
    buttons.rightPressTime = callTime;
  } else {
    buttons.paddleRight = false;
  }
}

#endif

// ---[ mpr121_read ]--------------------------------------------------------------------------------------------------------------------------
//     Purpose: Read a byte value from the mpr121.  FIXME: As this is only really used by the mpr121 to read the first two bytes, this function
//              may be optimized to read an integer without need for an address parameter or calling this function two times.
//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
#ifdef FEATURE_PADDLE_MPR121
byte mpr121_read(byte address)
{
  byte data;
  
  i2cSendStart();
  i2cWaitForComplete();
  
  i2cSendByte(MPR121_W);	// write 0xB4
  i2cWaitForComplete();
  
  i2cSendByte(address);	        // write register address
  i2cWaitForComplete();
  
  i2cSendStart();
  
  i2cSendByte(MPR121_R);	// write 0xB5
  i2cWaitForComplete();
  i2cReceiveByte(TRUE);
  i2cWaitForComplete();
  
  data = i2cGetReceivedByte();	// Get MSB result
  i2cWaitForComplete();
  i2cSendStop();
  
  cbi(TWCR, TWEN);	// Disable TWI
  sbi(TWCR, TWEN);	// Enable TWI
  
  return data;
}
#endif

// ---[ mpr121_write ]-------------------------------------------------------------------------------------------------------------------------
//     Purpose: Write a byte to the mpr12
//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
#ifdef FEATURE_PADDLE_MPR121
void mpr121_write(unsigned char address, unsigned char data)
{
  i2cSendStart();
  i2cWaitForComplete();
  
  i2cSendByte(MPR121_W);// write 0xB4
  i2cWaitForComplete();
  
  i2cSendByte(address);	// write register address
  i2cWaitForComplete();
  
  i2cSendByte(data);
  i2cWaitForComplete();
  
  i2cSendStop();
}
#endif

// ---[ setup_buttons ]------------------------------------------------------------------------------------------------------------------------
//     Purpose: Initialize the MPR121 capacitive touch controller
//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
#ifdef FEATURE_PADDLE_MPR121
void setup_buttons() {
  DDRC |= 0b00010011;
  PORTC = 0b00110000;  // Pull-ups on I2C Bus
  i2cInit();
  
  delay(100);
  
  // Section A
  // This group controls filtering when data is > baseline.
  mpr121_write(MHD_R, 0x01);
  mpr121_write(NHD_R, 0x01);
  mpr121_write(NCL_R, 0x00);
  mpr121_write(FDL_R, 0x00);

  // Section B
  // This group controls filtering when data is < baseline.
  mpr121_write(MHD_F, 0x01);
  mpr121_write(NHD_F, 0x01);
  mpr121_write(NCL_F, 0xFF);
  mpr121_write(FDL_F, 0x02);
  
  // Section C
  // This group sets touch and release thresholds for each electrode
  mpr121_write(ELE0_T, TOU_THRESH);
  mpr121_write(ELE0_R, REL_THRESH);
  mpr121_write(ELE1_T, TOU_THRESH);
  mpr121_write(ELE1_R, REL_THRESH);
  mpr121_write(ELE2_T, TOU_THRESH);
  mpr121_write(ELE2_R, REL_THRESH);
  mpr121_write(ELE3_T, TOU_THRESH);
  mpr121_write(ELE3_R, REL_THRESH);
  mpr121_write(ELE4_T, TOU_THRESH);
  mpr121_write(ELE4_R, REL_THRESH);
  mpr121_write(ELE5_T, TOU_THRESH);
  mpr121_write(ELE5_R, REL_THRESH);
  mpr121_write(ELE6_T, TOU_THRESH);
  mpr121_write(ELE6_R, REL_THRESH);
  mpr121_write(ELE7_T, TOU_THRESH);
  mpr121_write(ELE7_R, REL_THRESH);
  mpr121_write(ELE8_T, TOU_THRESH);
  mpr121_write(ELE8_R, REL_THRESH);
  mpr121_write(ELE9_T, TOU_THRESH);
  mpr121_write(ELE9_R, REL_THRESH);
  mpr121_write(ELE10_T, TOU_THRESH);
  mpr121_write(ELE10_R, REL_THRESH);
  mpr121_write(ELE11_T, TOU_THRESH);
  mpr121_write(ELE11_R, REL_THRESH);
  
  // Section D
  // Set the Filter Configuration
  // Set ESI2
  mpr121_write(FIL_CFG, 0x04);
  
  // Section E
  // Electrode Configuration
  // Enable 6 Electrodes and set to run mode
  // Set ELE_CFG to 0x00 to return to standby mode
  mpr121_write(ELE_CFG, 0x0C);	// Enables all 12 Electrodes
  //mpr121_write(ELE_CFG, 0x06);		// Enable first 6 electrodes
  
  // Section F
  // Enable Auto Config and auto Reconfig
  /*mpr121_write(ATO_CFG0, 0x0B);
  mpr121_write(ATO_CFGU, 0xC9);	// USL = (Vdd-0.7)/vdd*256 = 0xC9 @3.3V   mpr121_write(ATO_CFGL, 0x82);	// LSL = 0.65*USL = 0x82 @3.3V
  mpr121_write(ATO_CFGT, 0xB5);*/	// Target = 0.9*USL = 0xB5 @3.3V  
}
#else
// ---[ setup_buttons ]------------------------------------------------------------------------------------------------------------------------
//     Purpose: Initialize the hardware paddles
//   Changelog: 23-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void setup_buttons() {
  setup_pin(PIN_DIT_PADDLE, INPUT, HIGH);
  setup_pin(PIN_DAH_PADDLE, INPUT, HIGH);
}
#endif

// ---[ setup_pin ]----------------------------------------------------------------------------------------------------------------------------
//     Purpose: Set data pins up to known state.  Because of the number of pins used, this method saves on code space
//   Changelog: 18-NOV-2011 - IB - Initial Version
// --------------------------------------------------------------------------------------------------------------------------------------------
void setup_pin(byte pin,byte mode,byte level) {
  pinMode (pin,mode);
  digitalWrite (pin,level);
}
